/**
    ATClab - scriptable Air Traffic Control simulations.
    Copyright (C) 2010 openatclab@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

#include "canvasview.h"

#include "acsprite.h"
#include "actools.h"
#include "aircraft_agent.h"
#include "canvas.h"
#include "canvas_tools.h"
#include "listbox.h"
#include "waypointsprite.h"

#include "logging/logger.h"

#include <qapplication.h>
#include <qdatetime.h>
#include <qlabel.h>
#include <qkeysequence.h>
#include <qtimer.h>
#include <qmatrix.h>
#include <q3vbox.h>
//Added by qt3to4:
#include <QResizeEvent>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QEvent>

#include <assert.h>
#include <math.h>

#include <iostream>

using namespace atc;
using std::make_pair;
using std::string;


//------------------------------------------------------------------------------

/*!
 */
CanvasView::CanvasView( Q3Canvas* canvas, QWidget* parent, const char* name )
	  : Q3CanvasView( canvas, parent, name )
	  , _selectHandlers()
	  , _actionHandlers()
	  , _toolHandlers()
	  , _keyTools()
	  , _keyTags()
	  , _eventHandler(0)
	  , _tool( CanvasItem::NO_TOOL )
	  , _key( 0 )
	  , _pointer( false )
	  , _paused( false )
{
	//
	//
	//
	setVScrollBarMode( Q3ScrollView::AlwaysOff );
	setHScrollBarMode( Q3ScrollView::AlwaysOff );

	setFocusPolicy( Qt::StrongFocus );

	viewport()->setMouseTracking( true );

	connect( 
			this,   SIGNAL ( event_recorded( KeyValues & ) )
		  , canvas, SIGNAL ( event_recorded( KeyValues & ) )
	);
}


/*!
 */
CanvasView::~CanvasView() {}


//--------------------------------------------------------------------------------------------------
// [public] slots:
//

/*!
 *
 */
Canvas * CanvasView::atc_canvas() const {
	return dynamic_cast< Canvas * >( canvas() );
}


//--------------------------------------------------------------------------------------------------
//
//

/*!
 *
 */
//CanvasEventHandler* get_select_handler( CanvasItem::TYPE item ) { retrun _selectHandlers[item]; }

//--------------------------------------------------------------------------------------------------
//
//

/*!
 *
 */
void CanvasView::set_select_handler( CanvasItem::TYPE item, CanvasEventHandler *handler ) {
	_selectHandlers[item] = handler;
}

/*!
 *
 */
void CanvasView::set_action_handler( CanvasItem::TYPE item, CanvasEventHandler *handler ) {
	_actionHandlers[item] = handler;
}

/*!
 *
 */
void CanvasView::set_tool_handler( CanvasItem::TOOL item, CanvasEventHandler *handler ) {
	_toolHandlers[item] = handler;
}

/*!
 *
 */
void CanvasView::set_keyed_tools( const KeyToolMap &tools, const KeyTagMap &tags ) {
	_keyTools = KeyToolMap( tools );
	_keyTags = KeyTagMap( tags );
	//.insert( tools.begin(), tools.end() );
	//_keyTags.insert( tools.begin(), tags.end() );
}

//--------------------------------------------------------------------------------------------------
// 	[private]
//

/*!
 * Handle Event.
 *
 * Try to handle event with the current handler at the top of handler stack.
 * If current handler does not deal with the event try to load a new handler.
 * If no handlers available to deal with the event, ignore.
 */
void CanvasView::actionEvent( QEvent *e ) {
	if ( _paused ) return;
	if ( !_eventHandler || !_eventHandler->action(e) ) {
		if ( loadEventHandler(e) ) {
			_eventHandler->action(e);
		}
	}
}

/*!
 * Load a new event handler
 */
bool CanvasView::loadEventHandler( QEvent *e ) {
	QMouseEvent *me = dynamic_cast<QMouseEvent*>(e);
	if ( !me ) 
			return false;

	switch ( me->type() ) {

	case QEvent::MouseButtonPress:
		return _loadEventHandler( me->pos(), _selectHandlers );

	case QEvent::MouseButtonDblClick:
		return _loadEventHandler( me->pos(), _actionHandlers );

	default:
		return false;
	}
}

/*!
 *
 */
bool CanvasView::_loadEventHandler( const QPoint &p, HandlerMap &handlers ) {
	Q3CanvasItem *item = find_item( p );
	CanvasItem::TYPE type = 
			item	?	static_cast<CanvasItem::TYPE>(item->rtti()) 
					:	CanvasItem::RTTI_NONE;

	HandlerMap::iterator pos = handlers.find( type );
	if ( pos == handlers.end() || ! pos->second )
			return false;

	_eventHandler = pos->second;
	_eventHandler->setItem( item );
	return true;
}

/*!
 *
 */
bool CanvasView::loadToolHandler( CanvasItem::TOOL tool ) {
	_eventHandler = 0;
	if ( !_toolHandlers.count(tool) ) 
			return false;
	_eventHandler = _toolHandlers[tool];
	_eventHandler->onLoad();
	return true;
}


/*!
 *
 */
void CanvasView::unloadEventHandler( CanvasEventHandler *h ) {
	if ( h == _eventHandler ) _eventHandler= 0;
}


//------------------------------------------------------------------------------
// 	[protected] virtual QEvent event handlers 
//

/*!
 * Process key press events - used for tool manipulation.
 *
 * @param e QKeyEvent.
 */
void CanvasView::keyPressEvent( QKeyEvent* e ) {
	if ( _paused ) return;

	if ( Qt::NoButton == e->state() ) {
		set_tool_key( e->key() );
	}
	e->ignore();

	//
	// Log event
	//

	KeyValues kvs;
	kvs.push_back( make_pair( string( "event" ), to_string( "key_press" ) ) );
	kvs.push_back( make_pair( string( "key"   ), to_string( QString( QKeySequence( e->key() ) ).latin1() ) ) );
	record_event( kvs );
}

/*!
 * Virtual function to handle resizing of canvas view.
 *
 * @param e QResizeEvent defining resize event
 * 
 * @todo Set sprite drawing scale should be set for all sprites (class level) on
 * the basis of the view ie different views have different drawing scales. 
 * Sprites don't need independant drawing scales(??).
 */
void CanvasView::resizeEvent( QResizeEvent* e ) {
	QSize newsize =	e->size();
	
	QMatrix wm(worldMatrix());
	wm.reset();

	Q3Canvas* c = canvas();
	wm.scale(
			double(newsize.width()) / double(c->width())
		  ,	double(newsize.height()) / double(c->height())
	);
	setWorldMatrix( wm );
}


//------------------------------------------------------------------------------
// [private] implementation
//

/*!
 * Find atc::CanvasItem with higest order Z at cursor position
 *
 * \param pos is location in View coordinates.
 */
Q3CanvasItem* CanvasView::find_item( const QPoint &p ) {
	QPoint pt = inverseWorldMatrix().map(p);
	
	Q3CanvasItem *i = 0;
	Q3CanvasItemList items 
			= canvas()->collisions( QRect(pt-SNAP_SIZE,pt+SNAP_SIZE) );

	Q3CanvasItemList::iterator item = items.begin();
	for ( ; item != items.end(); ++item ) {
		if ( CanvasItem::RTTI_ACTIVE <= (*item)->rtti() ) {
			i = *item;
			break;
		}
	}

	return i;
}

//------------------------------------------------------------------------------


/*!
 *
void CanvasView::show_selector( 
		ListBox *selector, const QPoint &p, AircraftAgent *ac 
) {
	if ( _shown_selector ) {
		_shown_selector->clear_aircraft();
		_shown_selector->hide();
	}
	_shown_selector = selector;

	_shown_selector->set_aircraft( ac );

	// calc cavas view location
	QPoint pt = worldMatrix().map( p );
	pt -= QPoint( 
			_shown_selector->width() / 5
		  , _shown_selector->height() / 2 
	);

	//
	// log requirement request
	//
	KeyValues request;
	request.push_back( make_pair( string( "solution-request" ), to_string( _shown_selector->type()        ) ) );
	request.push_back( make_pair( string( "callsign"         ), to_string( _shown_selector->aircraft_id() ) ) );
	record_event( request );

	//
	// show dialog
	// 
	_shown_selector->move( pt );
	int result = _shown_selector->exec();

	//
	// log requirement
	//
	KeyValues kvs;
	kvs.push_back( make_pair( string( "solution" ), to_string( _shown_selector->type()        ) ) );
	kvs.push_back( make_pair( string( "callsign" ), to_string( _shown_selector->aircraft_id() ) ) );
	kvs.push_back( make_pair( string( "value"    ), to_string( _shown_selector->value()       ) ) );
	kvs.push_back( make_pair( string( "result"   ), to_string( result                         ) ) );
	record_event( kvs );

}
 */

/*!
 *
 */
void CanvasView::record_event( KeyValues &kvs ) {
	kvs.push_front( make_pair( string( "view" ), string( "experiment") ) );
	emit event_recorded( kvs );
}

/*!
 *
 */
void CanvasView::set_tool_key( int key ) {
	_key = key;

	_tool = (_keyTools)[ key ];
	loadToolHandler( _tool );
				
	atc_canvas()->set_tool( (_keyTags)[ key ].c_str() );
}


////////////////////////////////////////////////////////////////////////////////
